package cn.org.atool.generator.javafile;

import cn.org.atool.generator.database.config.IGlobalConfig;
import cn.org.atool.generator.database.config.IGlobalConfigSet;
import cn.org.atool.generator.database.config.ITableConfig;
import cn.org.atool.generator.database.config.ITableConfigSet;
import cn.org.atool.generator.database.config.impl.GlobalConfig;
import cn.org.atool.generator.database.config.impl.TableConfigSet;
import cn.org.atool.generator.database.config.impl.TableSetter;
import cn.org.atool.generator.javafile.summary.ATMFile;
import cn.org.atool.generator.javafile.template.*;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import static cn.org.atool.generator.util.GeneratorHelper.info;

/**
 * @author darui.wu
 */
@Getter
@Accessors(chain = true)
public class TemplateGenerator implements IGlobalConfig, ITableConfig {
    /**
     * 生成test辅助文件
     */
    protected boolean withTest = false;
    /**
     * 生成entity文件
     */
    protected boolean withEntity = false;

    protected List<TableConfigSet> tableConfigs = new ArrayList<>();
    /**
     * 全局配置
     */
    @Setter
    protected GlobalConfig globalConfig;

    public static IGlobalConfig build(boolean withEntity, boolean withTest) {
        if (!withEntity && !withTest) {
            throw new RuntimeException("At last one of Entity or Test generation must be true");
        }
        TemplateGenerator generator = new TemplateGenerator();
        generator.withTest = withTest;
        generator.withEntity = withEntity;
        return generator;
    }

    @Override
    public ITableConfig globalConfig(Consumer<IGlobalConfigSet> consumer) {
        this.globalConfig = new GlobalConfig();
        consumer.accept(this.globalConfig);
        return this;
    }

    @Override
    public ITableConfig tables(Consumer<ITableConfigSet> consumer) {
        TableConfigSet tableConfig = new TableConfigSet(this.globalConfig);
        consumer.accept(tableConfig);
        tables(tableConfig);
        return this;
    }

    public ITableConfig tables(ITableConfigSet... tableConfigs) {
        for (ITableConfigSet tableConfig : tableConfigs) {
            this.tableConfigs.add((TableConfigSet) tableConfig);
        }
        return this;
    }

    /**
     * 执行代码生成
     */
    @Override
    public void execute() {
        if (globalConfig == null) {
            throw new RuntimeException("The global config not set.");
        }
        if (tableConfigs.isEmpty()) {
            throw new RuntimeException("The table config is empty.");
        }
        List<TableSetter> tables = new ArrayList<>();
        for (TableConfigSet config : this.tableConfigs) {
            info("=== Database metadata initializing...");
            config.initTables();
            info("=== Preparing for file generation...");
            config.getTables().values().forEach(table -> {
                this.generateFiles(table);
                tables.add(table);
            });
            info("=== File generation finish !!!");
        }
        if (this.withTest) {
            this.generateSummary(tables);
        }
    }

    private void generateFiles(TableSetter table) {
        GlobalConfig gc = table.getGlobalConfig();

        info("====== Handling table: " + table.getTableName());
        info("====== Writing to " + table.getGlobalConfig().getTestOutputDir());
        if (withTest) {
            new DataMapFile(table).javaFile(gc.getTestOutputDir(), true);
            new TableMixFile(table).javaFile(gc.getTestOutputDir(), true);
        }
        if (withEntity) {
            new EntityFile(table).javaFile(gc.getOutputDir(), true);
            new DaoInterfaceFile(table).javaFile(gc.getDaoOutputDir(), false);
            new DaoImplementFile(table).javaFile(gc.getDaoOutputDir(), false);
        }
    }

    /**
     * 生成汇总文件
     *
     * @param tables
     */
    protected void generateSummary(List<TableSetter> tables) {
        new ATMFile(this.globalConfig.getBasePackage(), tables)
            .javaFile(this.globalConfig.getTestOutputDir(), true);
    }
}
